Apple, the Apple logo, and Macintosh are registered trademarks of Apple Computer, Inc.
Mac and OpenDoc are trademarks of Apple Computer, Inc.
There are several situations where parts may want to create offscreen canvases. Two common cases are: double-buffering, and image manipulation. Embedded parts are likely to create offscreen canvases so they may double-buffer output for greater display efficiency and quality. Containing parts may place embedded parts on offscreen canvases so they can graphically manipulate the imaging output of the embedded part, and perhaps combine it with their own.
Creating an Offscreen Canvas
Creating the Canvas
To create a canvas, a part must first use a particular graphics system to create a platform canvas. Then it can use the ODFacet::CreateCanvas() factory method to create an actual ODCanvas.
platformCanvas = MyCreatePlatformCanvas(); // as for your graphics system
newCanvas = facet->CreateCanvas(ev,
kGraphicsSystem, // enum code for your g.s.
platformCanvas,
kODTrue, // using a dynamic canvas
kODTrue); // this is OFFSCREEN
newCanvas->SetOwner(ev, fSelf); // fSelf is my PartWrapper
Putting the Canvas in a Facet
Containing Part
For a containing part to place an embedded part on an offscreen canvas, it must attach the canvas to the embedded facet. This must be done at facet creation time, before the embedded part gets noified of the new facet via FacetAdded(), where it may attempt to attach its own offscreen canvas (see below).
embeddedFacet = facet->CreateEmbeddedFacet(ev,
frame, clipShape, externalTransform,
newCanvas, // canvas created above
kODNULL, // no biasCanvas
kODNULL, kODInFront); // frontmost facet
If an already-existing embedded facet has no canvas of its own, the containing part may add one. In that case, embedded parts will be notified automatically that their display canvas has changed.
facet->SetCanvas(ev, newCanvas);
Embedded Part
An embedded part may attach a canvas to one of its facets at any time. During FacetAdded() is a good time. The part may also choose to move the facet to an offscreen canvas later, for whatever reasons.
facet->SetCanvas(ev, newCanvas);
If the facet already has a canvas (which must have been assigned by the containing part), the embedded part may not attach a canvas to that facet. If it still wants to image on its own canvas, it must create a subframe of the facet’s frame, create a facet on that subframe, and attach the canvas to that facet.
Imaging on an Offscreen Canvas
If your part is written correctly, you shouldn’t have to do anything different to image on an offscreen canvas. Some simple rules to follow:
• Obtain the canvas to image on from the facet passed as a parameter to Part::Draw(). This may be an offscreen canvas. Using QuickDraw on the Macintosh, get a GrafPtr from the canvas, and SetPort() to display on it; follow similar actions for different graphics systems.
• If a part needs to draw interactively (e.g. for rubber-banding or sweeping out a selection), it can image directly on the root facet’s canvas, which should be the same as the window’s display environment. All features of the canvas drawing environment computed by the facet are duplicated for the window environment: GetWindowFrameTransform, …ContentTransform, …AggregateClipShape. Use these values to image on the window environment.
• If you perform any imaging on your facet’s canvas asynchronously (e.g. at idle time instead of in response to a ::Draw() call), you must call ODFacet::DrawnIn() to allow proper updating of offscreen canvas contents to onscreen. If a part’s facet is moved to an offscreen canvas while it is running, it will receive a ::CanvasChanged() call, which will allow it to tranfer its asynchronous imaging to the new canvas.
• Use the Invalidate() and Validate() calls in ODFrame and ODFacet to mark whether areas on a canvas need repainting. These will either call the corresponding calls in the underlying graphics toolbox, or accumulate the invalid area on an offscreen canvas. This way, invalidation is performed the same way for on- or offscreen canvases.
Updating an Offscreen Canvas
The part which owns an offscreen canvas is responsible for tranferring its contents to its parent canvas. This is because only the part which created the canvas can be assumed to know how to tranfer its contents. The parent and child canvases may have different formats (e.g. bitmap vs. display list), or the owner may want to transform the canvas contents as it copies them (e.g. rotate or tint).
When a containing part has placed an embedded facet on an offscreen canvas, it should force the embedded part to draw before the containing part itself draws any of its own contents. This ensures that the contents of the offscreen canvas are up to date, and can safely be combined with the containing part’s contents. In the containing part’s ::Draw() method:
embeddedFacet->Draw(ev, invalidShape);
embeddedFacet->DrawChildren(ev, invalidShape); // in case it has embedded parts
If embedded parts display asynchronously, the containing part which owns the canvas on which they image will be notified via the ::CanvasUpdated() call. The part may transfer the content from the offscreen canvas at this time, or it may choose to defer it until later, for reasons of efficiency or minimizing excessive redrawing.